import math


def tsad_score(y_true, y_pred, pos_label=1, window='auto', alpha=1.8, beta=0.1, gamma=10):
    promptness = promptness_score(y_true, y_pred, pos_label=pos_label, window=window,
                                  alpha=alpha, beta=beta, gamma=gamma)
    continuity = continuity_score(y_true, y_pred, pos_label=pos_label, window=window,
                                  alpha=alpha, beta=beta, gamma=gamma)
    score = 2 * promptness * continuity / (promptness + continuity)
    return score


def promptness_score(y_true, y_pred, pos_label=1, window='auto', alpha=1.8, beta=0.1, gamma=10):
    if len(y_true) != len(y_pred):
        return "label length error"
    tp = tp_count(y_true, y_pred, pos_label=pos_label, window=window, alpha=alpha, beta=beta, gamma=gamma)
    true = len(get_position(y_true))
    score = tp / true
    return score


def continuity_score(y_true, y_pred, pos_label=1, window='auto', alpha=1.8, beta=0.1, gamma=10):
    if len(y_true) != len(y_pred):
        return "label length error"
    tp = tp_count(y_true, y_pred, pos_label=pos_label, window=window, alpha=alpha, beta=beta, gamma=gamma)
    positive_position = get_position(y_pred)
    true_position = get_position(y_true)
    window = get_window(y_true, pos_label)
    delete, append = [], []
    for true in true_position:
        t_start = true[0]
        t_end = true[1]
        for positive in positive_position:
            p_start = positive[0]
            p_end = positive[1]
            if p_end + window < t_start:
                continue
            elif p_start <= t_end:
                if true not in append:
                    append.append(true)
                if positive not in delete:
                    delete.append(positive)
            else:
                break
    for i in delete:
        positive_position.remove(i)
    for i in append:
        positive_position.append(i)
    score = tp / len(positive_position)
    return score


def tp_count(y_true, y_pred, pos_label=1, window='auto', alpha=1.8, beta=0.1, gamma=10):
    neg_label = 1 - pos_label
    if window == 'auto':
        window = get_window(y_true, pos_label)
    position = 0
    tp = 0
    while position < len(y_true):
        if y_true[position] == neg_label:
            if y_pred[position] == neg_label:
                position = position + 1
            elif y_pred[position] == pos_label:
                if pos_label in y_true[position:position + window + 1]:
                    tp = tp + 1
                    while position < len(y_true) and y_true[position] == neg_label:
                        position = position + 1
                    while position < len(y_true) and y_true[position] == pos_label:
                        position = position + 1
                else:
                    position = position + 1
            else:
                return "label value must be 0 or 1"
        elif y_true[position] == pos_label:
            if y_pred[position] == pos_label:
                tp = tp + 1
                while position < len(y_true) and y_true[position] == pos_label:
                    position = position + 1
            elif y_pred[position] == neg_label:
                delay = 0
                start = position
                while position < len(y_true) and y_true[position] == pos_label:
                    if y_pred[position] == neg_label:
                        position = position + 1
                        delay = delay + 1
                    else:
                        while position < len(y_true) and y_true[position] == pos_label:
                            position = position + 1
                        end = position
                        value = alpha / (1 + math.exp(delay / (end - start) * gamma)) + beta
                        tp = tp + value
        else:
            return "label value must be 0 or 1"
    return tp


def get_window(y_true, pos_label):
    pos_min_len = len(y_true)
    neg_label = 1 - pos_label
    pos_len = 0
    for i in y_true:
        if i == neg_label:
            if 1 <= pos_len < pos_min_len:
                pos_min_len = pos_len
            pos_len = 0
        elif i == pos_label:
            pos_len = pos_len + 1
        else:
            return "label value must be 0 or 1"
    if pos_min_len == 1:
        window = 1
    else:
        window = max(pos_min_len // 10, 2)
    return window


def get_position(label):
    res, i = [], 0
    while i < len(label):
        try:
            while label[i] == 0:
                i = i + 1
            start = i
            while label[i] == 1:
                i = i + 1
            end = i
            res.append([start, end])
        except:
            break
    return res


if __name__ == "__main__":
    y_true_test = [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0,
                   0,
                   0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0]
    # y_pred = [random.randint(0, 1) for i in range(len(y_true))]
    y_pred_test = [0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
                   0,
                   0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0]
    print(tsad_score(y_true_test, y_pred_test))
    print(continuity_score(y_true_test, y_pred_test))
    print(promptness_score(y_true_test, y_pred_test))
